package com.my.aslife.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.my.aslife.module.common.*;
import com.my.aslife.module.entity.User;
import com.my.aslife.module.entity.dtos.*;
import com.my.aslife.exception.BusinessException;
import com.my.aslife.mapper.UserMapper;
import com.my.aslife.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.my.aslife.service.TxMoneyService;
import com.my.aslife.utils.AppJwtUtil;
import com.my.aslife.utils.CheckSensitiveUtil;
import com.my.aslife.utils.Encryption;
import com.my.aslife.utils.MyCheckUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author jobob
 * @since 2022-12-12
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    @Lazy
    private TxMoneyService txMoneyService;

    @Override
    public ResponseResult<List<User>> getUserListByUIDs(List<Long> ids) {

        List<User> users = userMapper.getUserListByUIDs(ids);

        return ResponseResult.ok(users);
    }

    @Override
    public ResponseResult setUserIllegalStatus(Long uid) {
        userMapper.setUserIllegalStatus(uid);
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult queryUserInfoByUID(Long uid) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getUid, uid);

        return ResponseResult.ok(getOne(lqw));
    }

    @Override
    @Transactional
    public ResponseResult deductMoney(DeductMoneyDto deductMoneyDto) {
        Integer uid = UserIdThreadLocal.get();
        User user = queryUserById(uid);
        // 扣款失败
        if (user.getMoney() < deductMoneyDto.getMoney()) {
            return ResponseResult.failed(ErrorCode.MONEY_NOT_GOOD_ENOUGH);
        }
        userMapper.deductMoney(deductMoneyDto);
        txMoneyService.save(deductMoneyDto.getTxMoney());

        return ResponseResult.ok();
    }

    /**
     * @param user 用户
     * @return ResponseResult
     */
    @Override
    public ResponseResult login(UserLoginDto user) {

        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getUserName, user.getUserName());
        User queryUser = userMapper.queryUserByUserName(user.getUserName());
        // 用户不存在
        if (queryUser == null) {
            return ResponseResult.failed(ErrorCode.TAIL_LOGIN_NO_USERNAME);
        }
        String password = queryUser.getPassword();

        Integer status = queryUser.getStatus();
        if (!user.getPassword().equals(password)) {
            return ResponseResult.failed(ErrorCode.FAIL_LOGIN_ERROR_USERNAME_PASSWORD);
        }
        // 登录判断违规
        if (status == 1) {
            return ResponseResult.failed(ErrorCode.FAIL_LOGIN_ILLEGAL);
        }

        Map<String, String> map = returnUserData(queryUser);

        return ResponseResult.ok(map);
    }

    @Override
    public ResponseResult getUserInfoByPage(PageRequestVo pageRequestVo) {

        int c = pageRequestVo.getCurrent();
        int p = pageRequestVo.getPageSize();
        int start = (c - 1) * p;

        List<User> users = userMapper.getUserInfoByPage(start, p, pageRequestVo.getCondition());

        int count = userMapper.getQueryCount(pageRequestVo.getCondition());

        List<UserDto> userDtoList = new ArrayList<>(users.size());
        users.forEach(user -> {
            UserDto userDto = new UserDto();
            BeanUtils.copyProperties(user, userDto);
            userDto.setTypeName(MyCheckUtils.typeToTypeName(userDto.getType()));
            userDtoList.add(userDto);
        });

        return ResponseResult.ok(userDtoList, count);
    }

    @Override
    public Boolean registerByUserName(User user) {
        String userName = user.getUserName();
        User checkUser = queryUserByUserName(userName);
        if (checkUser != null && !StrUtil.isBlank(checkUser.getUserName())) {
            throw new BusinessException(ErrorCode.ERROR_REGISTER_USERNAME_YES);
        }
        try {
            String bash64AndMD5 = Encryption.getBash64AndMD5(user.getPassword());
            user.setPassword(bash64AndMD5);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return save(user);
    }

    @Override
    public User queryUserById(Integer id) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getUid, id);

        return getOne(lqw);
    }

    @Override
    public User queryUserByEmailOrUserName(String emailOrUserName) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();

        lqw.eq(User::getUserName, emailOrUserName);

        return getOne(lqw);
    }

    @Override
    public ResponseResult registerByEmail(RegisterDto registerDto) {

        // 判断验证码是否存在，或不正确
        User user = registerDto.getUser();
        String userNameKey = RedisConstant.EMAIL_REGISTER_CODE + user.getUserName();
        String code = stringRedisTemplate.opsForValue().get(userNameKey);
        if (code == null || !code.equals(registerDto.getCode())) {
            return ResponseResult.failed(ErrorCode.VERITY_CODE_ERROR);
        }

        // 判断用户名是否存在
        Integer userFlag = userMapper.getUserNameFlag(user.getUserName());
        if (userFlag > 0) {
            return ResponseResult.failed(ErrorCode.EMAIL_OR_USER_NAME_EXISTS);
        }
        user.setEmail(user.getUserName());
        // 保存用户
        save(user);
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult changeUserStatus(Integer uid) {

        // 修改用户状态 太快限制
        String key = stringRedisTemplate.opsForValue().get(RedisConstant.CHANGE_STATUS_KEY + uid);
        if (key != null) {
            throw new BusinessException(ErrorCode.OPERATOR_FAST);
        }
        boolean isOk = userMapper.changeUserStatus(uid);
        // 是否修改成功
        if (!isOk) {
            throw new BusinessException(ErrorCode.ERROR_SYSTEM_BUSY);
        }
        stringRedisTemplate.opsForValue().set(RedisConstant.CHANGE_STATUS_KEY + uid, "0",
                RedisConstant.CHANGE_STATUS_TTL, TimeUnit.MINUTES);

        return ResponseResult.ok(null);
    }

    @Override
    public ResponseResult deleteBatchByIds(String[] ids) {
        Integer integer = UserIdThreadLocal.get();

        // 判断是否操作过
        String queryAuthority = stringRedisTemplate.opsForValue().get(RedisConstant.AUTHORITY_NOT_DELETE+integer);
        if (queryAuthority != null) {
            return ResponseResult.failed(ErrorCode.NOT_DELETE_AUTHORITY);
        }

        // 判断是否有删除权限
        User user = getById(integer);
        if (!Objects.equals(user.getType(), TypeName.TYPE_99.getType())) {
            // 加入redis
            stringRedisTemplate.opsForValue().set(RedisConstant.AUTHORITY_NOT_DELETE+integer, "");
            return ResponseResult.failed(ErrorCode.NOT_DELETE_AUTHORITY);
        }

        userMapper.deleteBatchByIds(Arrays.asList(ids));
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult changeUserType(UserTypeDto userTypeDto) {
        UserTypeDto operatorUser = userMapper.getUserTypeByUID(UserIdThreadLocal.get());
        // 权限不足
        if (operatorUser.getType() != 99) {
            return ResponseResult.failed(ErrorCode.NOT_AUTHORITY);
        }
        userMapper.changeUserType(userTypeDto);

        return ResponseResult.ok(null);
    }

    @Override
    public ResponseResult ResponseResult(ResetDto resetDto) {

        String key = RedisConstant.EMAIL_RESET_PWD_CODE + resetDto.getUser().getUserName();
        String code = stringRedisTemplate.opsForValue().get(key);
        // 邮件验证码过期
        if (code == null) {
            return ResponseResult.failed(ErrorCode.EMAIL_CODE_EXPIRE);
        }
        // 验证码错误
        if (!code.equals(resetDto.getCode())) {
            return ResponseResult.failed(ErrorCode.VERITY_CODE_ERROR);
        }

        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        User user = resetDto.getUser();
        lqw.eq(User::getUserName, user.getUserName());
        User queryUser = getOne(lqw);
        // 密码与原密码相同
        if(queryUser.getPassword().equals(user.getPassword())) {
            return ResponseResult.failed(ErrorCode.OLD_PWD_IS_NEW_PWD);
        }
        userMapper.updatePassword(user);
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult loginByEmailCode(UserLoginByEmailDto user) {
        String key = RedisConstant.EMAIL_LOGIN_CODE + user.getEmail();
        String getCode = stringRedisTemplate.opsForValue().get(key);
        if (!user.getCode().equals(getCode)) {
            return ResponseResult.failed(ErrorCode.VERITY_CODE_ERROR);
        }

        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getEmail, user.getEmail());
        User queryUserByEmail = getOne(lqw);

        Map<String, String> map = returnUserData(queryUserByEmail);

        return ResponseResult.ok(map);
    }

    @Override
    public ResponseResult changeUserNickByUID(UserNickDto userNickDto) {
        Map<String, Integer> stringIntegerMap = CheckSensitiveUtil.matchWords(userNickDto.getNickName());
        // 一周只能修改一次
        String userNicKKey = RedisConstant.USER_CHANGE_NICKNAME + userNickDto.getUid();
        String flag = stringRedisTemplate.opsForValue().get(userNicKKey);
        if (flag != null) {
            return ResponseResult.failed(ErrorCode.NOT_CHANGE_NICKNAME);
        }
        // 昵称包含敏感词
        if (stringIntegerMap.size() != 0) {
            return ResponseResult.failed(ErrorCode.NICKNAME_HAVE_SENSITIVE);
        }
        userMapper.changeUserNickByUID(userNickDto);
        stringRedisTemplate.opsForValue().set(userNicKKey, " ", RedisConstant.ONE_WEEK, TimeUnit.DAYS);

        return ResponseResult.ok();
    }

    @Override
    public void txMoney(User user) {
        userMapper.txMoney(user);

    }

    @Override
    public void jiaMoney(int i) {
        User user = new User();
        user.setMoney(i);
        user.setUid(UserIdThreadLocal.get());
        userMapper.jiaMoney(user);
    }

    @Override
    public ResponseResult addUserInfo(User user) {
        userMapper.addUserInfo(user);
        return ResponseResult.ok();
    }

    @Override
    public void updateUserRunner(User user) {
        userMapper.updateUserRunner(user);
    }


    public User queryUserByUserName(String userName) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(!StrUtil.isBlank(userName), User::getUserName, userName);

        return getOne(lqw);
    }

    private Map<String, String> returnUserData(User queryUser){
        String token = AppJwtUtil.getToken(Long.parseLong(queryUser.getUid() + ""));

        // 保存用户token
        stringRedisTemplate.opsForValue().set(RedisConstant.LOGIN_USER_TOKEN + queryUser.getUid(),
                token, RedisConstant.ONE_DAY, TimeUnit.DAYS);
        Map<String, String> map = new HashMap<>();
        map.put("userId", queryUser.getUid() + "");
        map.put("token", token);
        map.put("money", queryUser.getMoney()+"");
        map.put("userName", queryUser.getNickName());
        map.put("type", queryUser.getType() + "");
        map.put("avatarUrl", Constant.AVATAR_URL);
        return map;
    }
}
